package edu.nf.ioc;

import edu.nf.ioc.annotation.Component;
import edu.nf.ioc.annotation.Scope;
import edu.nf.ioc.util.BeanNameUtils;
import edu.nf.ioc.util.ScanUtils;
import io.github.classgraph.ClassInfoList;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @author wangl
 */
public class BeanFactory {
    /**
     * 存放bean的描述
     */
    private final Map<String, BeanDefinition> definitionMap = new ConcurrentHashMap<>();

    /**
     * 存放单例bean的实例
     */
    private final Map<String, Object> singletonCache = new ConcurrentHashMap<>();

    private final static String SINGLETON_SCOPE = "singleton";
    private final static String PROTOTYPE_SCOPE = "prototype";

    /**
     * 在构造方法中初始化并构建所有bean描述
     * 以及单例的bean
     *
     * @param path 扫描路径
     */
    public BeanFactory(String path) {
        ClassInfoList classInfoList = ScanUtils.scan(path);
        //初始化原型
        initDefinitionMap(classInfoList);
        //初始化单例
        initSingleton();
        //执行singleton实例装配
        assemblySingletons();
    }

    /**
     * 根据扫描的类名进行解析，找出带有@Component注解的类，并构建成
     * BeanDefinition实例，保存到definitionMap集合中
     */
    private void initDefinitionMap(ClassInfoList classInfoList) {
        classInfoList.forEach(classInfo -> {
            Class<?> beanClass = classInfo.loadClass();
            //检查beanClass是否标注了@Component注解
            if (beanClass.isAnnotationPresent(Component.class)) {
                //获取@Component注解的value属性的值，这个值作为bean在容器的唯一标识
                String beanName = beanClass.getAnnotation(Component.class).value();
                //如果没有设置value，默认将类名作为beanName，并将类名首字母变为小写
                beanName = "".equals(beanName) ? BeanNameUtils.toLowerBeanName(beanClass.getSimpleName()) : beanName;
                //如果容器已经存在bean，则抛出异常
                if (definitionMap.containsKey(beanName)) {
                    throw new RuntimeException(
                            "conflicts with existing, non-compatible bean definition of same name and class ["
                                    + beanClass + "]");
                } else {
                    definitionMap.put(beanName, createBeanDefinition(beanClass));
                }
            }
        });
    }

    /**
     * 构建bean描述定义,将bean的scope以及类名封装到BeanDefinition中
     * 创建的Bean描述会放入definitionMap的集合中保存
     * Bean的类名作为集合的key,而整个BeanDefinition对象作为value
     *
     * @param beanClass
     */
    private BeanDefinition createBeanDefinition(Class<?> beanClass) {
        //创建BeanDefinition
        BeanDefinition definition = new BeanDefinition();
        //设置Bean的Class对象
        definition.setBeanClass(beanClass);
        //设置Bean的作用域
        definition.setSingleton(getBeanScope(beanClass));
        return definition;
    }

    /**
     * 解析Scope注解，如果Bean的class上没有定义@Scope注解，那么默认就表示单例
     * 如果bean的class上指定了Scope注解,则将@Scope的value属性值作为Bean的创建方式
     * 否则Bean的默认创建方式将使用单例
     */
    private boolean getBeanScope(Class<?> beanClass) {
        if(!beanClass.isAnnotationPresent(Scope.class)) {
            return true;
        }
        String scope = beanClass.getAnnotation(Scope.class).value();
        if(SINGLETON_SCOPE.equals(scope)) {
            return true;
        }else if(PROTOTYPE_SCOPE.equals(scope)) {
            return false;
        } else {
            throw new RuntimeException("This scope is not supported.");
        }
    }

    /**
     * 初始化SINGLETON实例放入bean容器中
     */
    private void initSingleton() {
        for (String beanName : definitionMap.keySet()) {
            BeanDefinition definition = definitionMap.get(beanName);
            if (definition.isSingleton()) {
                Object bean = newInstance(definition);
                singletonCache.put(beanName, bean);
            }
        }
    }

    /**
     * 为所有singleton实例执行装配（依赖注入）
     */
    private void assemblySingletons() {
        for (String beanName : singletonCache.keySet()) {
            Class<?> beanClass = definitionMap.get(beanName).getBeanClass();
            Object bean = singletonCache.get(beanName);
            InjectHandlerContext.inject(bean, beanClass, this);
        }
    }

    /**
     * 为prototype实例执行装配
     */
    protected Object assemblyPrototype(BeanDefinition definition){
        Object bean = newInstance(definition);
        InjectHandlerContext.inject(bean, definition.getBeanClass(), this);
        return bean;
    }

    /**
     * 根据描述定义创建Bean实例
     * @param definition
     * @return
     */
    private Object newInstance(BeanDefinition definition) {
        try {
            return definition.getBeanClass().newInstance();
        } catch (InstantiationException e) {
            throw new RuntimeException("Create bean instance fail.", e);
        } catch (IllegalAccessException e) {
            throw new RuntimeException("Create bean instance fail.", e);
        }
    }

    /**
     * 获取bean实例
     *
     * @param beanName
     * @return
     */
    public Object getBean(String beanName) {
        return doGetBean(beanName);
    }

    /**
     * 获取bean实例(泛型)
     *
     * @param beanName
     * @param clazz
     * @return
     */
    @SuppressWarnings("unchecked")
    public <T> T getBean(String beanName, Class<T> clazz) {
        return (T) doGetBean(beanName);
    }

    /**
     * 从容器中获取Bean的BeanDefinition
     * 如果Bean为singleton,则从singletonMap中获取单例
     * 否则以原型的方式创建并返回
     */
    private Object doGetBean(String beanName) {
        Object instance = singletonCache.get(beanName);
        if(instance != null){
            return instance;
        }
        return assemblyPrototype(definitionMap.get(beanName));
    }

    public void close(){
        // 清空Bean描述集合
        definitionMap.clear();
        // 清空Bean实例集合
        singletonCache.clear();
    }
}
